#include <string>
#include <iostream>

#include <nlohmann/json.hpp>
using json = nlohmann::json;

#include "digital_world.hpp"

namespace dw 
{
    void to_json(json& j, const Map::State& state) {
        //游程编码
        std::string compressed_map;
        compressed_map = state.compress();

        j = json{
            {"id", state.id},
            {"row", state.row},
            {"col", state.col},
            {"map", compressed_map}
        };
    }

    void from_json(const json& j, Map::State& state) {
        std::string compressed_map;

        j.at("id").get_to(state.id);
        j.at("row").get_to(state.row);
        j.at("col").get_to(state.col);
        j.at("map").get_to(compressed_map);

        state.decompress(compressed_map);
    }

    void to_json(json &j, const Map &map) {
        to_json(j, map.getMapState());
    }

    void from_json(const json &j, Map &map) {
        Map::State state;
        from_json(j, state);
        map.set(state);
    }

    void to_json(json& j, const Agent::State& state) {
        Coord dir = state.vDirection;
        dir = dir.polar();
        double dir_angle = dir.y;

        j = json {
            {"id", state.id},
            {"shape", state.shape},
            {"radius", state.dRadius},
            {"direction", dir_angle},
            {"velocity", state.dVelocity},
            {"angularVelocity", state.dOmega},
            {"acceleration", state.dAcceleration},
            {"position", {
                    {"x", state.vPosition.x}, 
                    {"y", state.vPosition.y}
                }
            }
        };
    }

    void from_json(const json &j, Agent::State& state) {
        double angle = j["direction"];
        state.vDirection = Coord(1, angle).cart();

        state.id = j["id"];
        state.shape = Agent::Shape(j["shape"]);
        state.dRadius = j["radius"];
        state.dVelocity = j["velocity"];
        state.dOmega = j["angularVelocity"];
        state.dAcceleration = j["acceleration"];
        state.vPosition = Coord({j["position"]["x"], j["position"]["y"]});
    }

    void to_json(json &j, const Agent &agent) {
        Agent::State state = agent.state;
        to_json(j, state);
    }

    void from_json(const json &j, Agent& agent) {
        Agent::State state;
        from_json(j, state);
        agent = Agent(state);
    }

    void to_json(json &j, const AgentManager& agent_manager) {
        j = json::array();

        for (auto agent : agent_manager.pAgents) {
            json jagent;
            to_json(jagent, *(agent.second));
            j += jagent;
        }
    }

    void from_json(const json &j, AgentManager& agent_manager) {
        for (auto jagent : j) {
            Agent::State state;
            from_json(jagent, state);
            std::shared_ptr<Agent> p = std::make_shared<Agent>(state);
            agent_manager.pAgents.insert({state.id, std::move(p)});
        }
    }

    void to_json(json& j, const Path& path) {
        int cnt = 0;

        j["nodes"] = json::array();
        for (auto coord : path.m_path) {
            cnt++;
            j["nodes"] += json{
                {"x", coord.x},
                {"y", coord.y}
            };
        }
        j["length"] = cnt;
    }

    void from_json(const json& j, Path& path) {
        json nodes = j["nodes"];
        for (auto jcoord : nodes) {
            path.m_path.emplace_back(Coord({jcoord["x"], jcoord["y"]}));
        }
    }

    void to_json(json& j, const World& world) {
        json& map = j["map"];
        json& paths = j["paths"];
        json &agents = j["agents"];

        to_json(agents, world.agent_manager);
        paths = json::array();
        for (auto p : world.scheduler) {
            json path;
            to_json(path, *p.second);
            paths += path;
        }
    }

    void from_json(const json& j, World& world) {
        json map = j["map"];
        json agents = j["agents"];
        json paths = j["paths"];

        from_json(agents, world.agent_manager);
        uint32_t id = 0;
        for (auto p : paths)
        {
            ++id;
            std::shared_ptr<Path> path = std::make_shared<Path>();
            from_json(p, *path);
            world.scheduler.insert({id, std::move(path)});
        }
    }
}